using System;
using System.Globalization;
using System.Web;
using System.Xml;

/*	-----------------------------------------------------------------------	
	Copyright:	umlungu consulting (pty) ltd
	Author:		Alan Benington	
	Started:	2007-06-22	
	Status:		beta	
	Version:	2.0.0
	Build:		20070730
	License:	GNU General Public License
	-----------------------------------------------------------------------	*/

/*	-----------------------------------------------------------------------	
	Development Notes:
	==================
	20070622:	Starting point.
	20070730:	Add set functionality
	-----------------------------------------------------------------------	*/

namespace umlungu.engineX.scorecardX {
	/// <summary>
	/// The x_scorecard class describes and contains a scorecard tree.
	/// It is the core class for all scorecarding applications and services.
	/// Typically this class is instantiated by a webservice to provide specific scorecard services.
	/// </summary>
	public class x_scorecard : umlungu.engineX.x_tree {
		#region Invisible properties
		private const bool expand = false;
		private x_score scorex;
		#endregion

		#region Constants
		private const string NODE = "score";
		private const string NODE_NAME = "name";
		private const string NODE_VALUE = "value";
		private const string NODE_WEIGHT = "weight";
		private const string TYPE_CARD = "card";
		private const string TYPE_SETS = "sets";
		private const string TYPE_SET = "set";
		private const string TYPE_GROUP = "group";
		private const string TYPE_SCORE = "not(@type)";
		private const string NODE_PROPERTY = "property";
		private const string NODE_PROPERTY_VALUES = "values";
		private const string NODE_PROPERTY_VALUE = "value";
		#endregion

		#region Error Constants
		private const string ERROR_SET_EXISTS = "The score sets already exists.";
		private const string ERROR_SET_NOTEXIST = "The score sets does not exist.";
		#endregion

		#region Visible properties
		#endregion

		#region Constructors/Destructors
		/// <overloads>Constructor</overloads>
		/// <summary>Default constructor</summary>
		public x_scorecard() : base(NODE) {
			scorex = new x_score();
		}
		/// <summary>Constructor description</summary>
		/// <param name="file">The file to acquire on creation of the object</param>
		public x_scorecard(string file) : base(NODE, file) {
			scorex = new x_score();
		}
		#endregion

		#region Public methods
		/// <summary>Lists the scorecards</summary>
		public void LoadCards(bool expandall) {
			GetNodes("card", expandall);
		}

		/// <summary>Lists the scorecards</summary>
		public XmlNodeList ListCards() {
			return GetNodes("card", expand);
		}

		/// <summary>Gets the default scorecard</summary>
		public XmlNode GetTemplate() {
			return GetNode(TYPE_CARD, "default", false);
		}

		/// <summary>Gets a scorecard</summary>
		public XmlNode GetCard(string CardName) {
			 return GetNode(TYPE_CARD, CardName, true);
		}

		/// <summary>Creates a new scorecard</summary>
		public void NewCard(string CardName, string CardFile) {
			XmlNode template = GetNode(TYPE_CARD, "default", false);
			XmlNode newcard = AddNode(DocumentElement, TYPE_CARD, CardName, CardFile);
			newcard.InnerXml = template.InnerXml;
			Expanded(newcard);
		}

		/// <summary>Adds a new scorecard to an existing</summary>
		public void AddCard(string Parent, string CardName) {
			XmlNode template = GetNode(TYPE_CARD, Parent, true);
			XmlNode newcard = AddNode(template, TYPE_CARD, CardName);
			newcard.InnerXml = template.InnerXml;
		}

		/// <summary>Removes an existing scorecard</summary>
		public void DeleteCard(string CardName) {
			XmlNode thiscard = GetCard(CardName);
			if (thiscard != null) {
				thiscard.ParentNode.RemoveChild(thiscard);
			}
		}

		/// <summary>Removes an existing scorecard</summary>
		public void UpdateCard(string CardName, string CardXml) {
			XmlNode thiscard = GetCard(CardName);
			if (thiscard != null) {
				XmlDocument tmpcard = new XmlDocument();
				tmpcard.LoadXml(CardXml);
				if (tmpcard.DocumentElement.Name == NODE) {
					thiscard.InnerXml = tmpcard.DocumentElement.InnerXml;
					_SetChanged(thiscard as XmlElement);
				}
			}
		}

		/// <summary>Saves all changed scorecards to file</summary>
		public void SaveCards(string filename) {
			Persist(filename, TYPE_CARD);
		}
		public void SaveCards(string filename, string CardName) {
			Persist(filename, TYPE_CARD, CardName);
		}
		/// <summary>Saves all scorecards to file</summary>
		public void SaveCards(string filename, bool all) {
			Persist(filename, TYPE_CARD, all);
		}

		/// <summary>Adds a new property to an existing scorecard</summary>
		public void AddProperty(string CardName, string PropName, string PropLabel) {
			XmlNode scorecard = GetNode(TYPE_CARD, CardName, true);
			XmlElement property = scorecard.AppendChild(CreateElement(NODE_PROPERTY)) as XmlElement;
			property.SetAttribute("name", PropName);
			property.InnerText = PropLabel;
			property.AppendChild(CreateElement(NODE_PROPERTY_VALUES));
		}

		/// <summary>Removes a property from an existing scorecard</summary>
		public void DeleteProperty(string CardName, string PropName) {
			XmlNode propnode = GetChild(CardName, NODE_PROPERTY, PropName, true);
			if (propnode != null) {
				XmlElement cardel = propnode.ParentNode as XmlElement;
				cardel.RemoveChild(propnode);
				_SetChanged(cardel);
			}
		}

		/// <summary>Gets a property in an existing scorecard case</summary>
		public XmlNode GetProperty(string CardName, string PropName) {
			return GetChild(CardName, NODE_PROPERTY, PropName, true);
		}

		/// <summary>Gets the properties in an existing scorecard case</summary>
		public XmlNodeList GetProperties(string CardName) {
			XmlNode card = GetCard(CardName);
			return card.SelectNodes(NODE_PROPERTY);
		}

		/// <summary>Removes values from a property in an existing scorecard</summary>
		public void ClearProperty(string CardName, string PropName) {
			XmlNode propnode = GetChild(CardName, NODE_PROPERTY, PropName, true);
			XmlElement cardel = propnode.ParentNode as XmlElement;
			if (propnode != null) {
				foreach (XmlNode valnode in propnode.SelectNodes("values/value")) {
					valnode.ParentNode.RemoveChild(valnode);
				}
			}
			_SetChanged(cardel);
		}

		/// <summary>Adds a new property to an existing scorecard</summary>
		public void AddPropertyValue(string CardName, string PropName, string PropValue) {
			XmlNode propnode = GetChild(CardName, NODE_PROPERTY, PropName, true);
			XmlNode values = propnode.SelectSingleNode(NODE_PROPERTY_VALUES);
			XmlNode value = values.AppendChild(CreateElement(NODE_PROPERTY_VALUE));
			value.InnerText = PropValue;
		}

		/// <summary>Gets a score</summary>
		public XmlNode GetScore(string CardName, string ScoreName) {
			GetNode(TYPE_CARD, CardName, true);
			return GetNode(ScoreName, true);
		}
		
		/// <summary>Adds a new score to an existing scorecard</summary>
		public XmlNode AddScore(string CardName, string GroupName, string ScoreName, string ScoreLabel) {
			XmlNode scorecard = GetNode(TYPE_CARD, CardName, true);
			XmlNode group = GetNode(TYPE_GROUP, GroupName, true);
			XmlElement score = group.AppendChild(CreateElement(NODE)) as XmlElement;
			score.SetAttribute("name", ScoreName);
			score.InnerText = ScoreLabel;
			return score;
		}
		
		/// <summary>Removes a score from an existing scorecard</summary>
		public void DeleteScore(string CardName, string ScoreName) {
			XmlNode scorenode = GetScore(CardName, ScoreName);
			if (scorenode != null)
				scorenode.ParentNode.RemoveChild(scorenode);
		}

		/// <summary>Adds a new weight to an existing scorecard</summary>
		public XmlNode AddScoreWeight(string CardName, string ScoreName, string ScoreWeight) {
			return AddScoreValue(CardName, ScoreName, "", ScoreWeight);
		}
		/// <summary>Adds a new score to an existing scorecard</summary>
		public XmlNode AddScoreValue(string CardName, string ScoreName, string ScoreValue) {
			return AddScoreValue(CardName, ScoreName, ScoreValue, "");
		}
		/// <summary>Adds a new score and weight to an existing scorecard</summary>
		public XmlNode AddScoreValue(string CardName, string ScoreName, string ScoreValue, string ScoreWeight) {
			XmlElement score = GetScore(CardName, ScoreName) as XmlElement;
			if (ScoreValue != "")
				score.SetAttribute(NODE_VALUE, ScoreValue);
			if (ScoreWeight != "")
				score.SetAttribute(NODE_WEIGHT, ScoreWeight);
			return score;
		}

		/// <summary>Removes a score from an existing scorecard</summary>
		public void DeleteScoreValue(string CardName, string ScoreName) {
			XmlElement score = GetScore(CardName, ScoreName) as XmlElement;
			score.RemoveAttribute(NODE_VALUE);
		}

		/// <summary>Removes a score from an existing scorecard</summary>
		public void DeleteScoreWeight(string CardName, string ScoreName) {
			XmlElement score = GetScore(CardName, ScoreName) as XmlElement;
			score.RemoveAttribute(NODE_WEIGHT);
		}

		/// <summary>Saves ID's assigned to nodes</summary>
		public void RemoveIDs() {
			removeIDs();
		}

		/// <summary>Lists the scoresets - not complete</summary>
		/// <remarks>There might be some implementation specific properties here - maybe need to move the defintions of these to a higher level</remarks>
		public XmlNodeList ListSets(string CardName) {
			XmlNodeList setlist = GetNodes(TYPE_SET, true);
			int i = 0;
			foreach (XmlNode set in setlist) {
				XmlElement setel = set as XmlElement;
				scorex.Load(set);
				scorex.Calculate();
				i++;
				setel.SetAttribute("id", i.ToString());
				setel.SetAttribute("auditor", GetProperty(set, "auditors"));
				setel.SetAttribute("team", GetProperty(set, "teams"));
				setel.SetAttribute("channels", GetProperties(set, "channels"));
				setel.SetAttribute("score", scorex.Score);
			}
			return setlist;
		}

		/// <summary>Gets a scorecard</summary>
		public XmlNode GetSet(string CardName, string SetName) {
			XmlNode scoresets = GetSets(CardName);
			XmlNode set = GetNode(scoresets, TYPE_SET, SetName, true);
			scorex.Load(set);
			scorex.Calculate();
			return set;
		}

		/// <summary>Gets a scorecard</summary>
		public XmlNode GetSets(string CardName) {
			XmlNode scorecard = GetNode(TYPE_CARD, CardName, true);
			XmlNode scoresets = GetNode(scorecard, TYPE_SETS, false);
			if (scoresets == null)
				scoresets = AddNode(scorecard, TYPE_SETS, "active");
			else {
				scorex.Load(scoresets);
				scorex.Calculate();
				/*
				foreach (XmlNode set in GetNodes(TYPE_SET, scoresets)) {
					scorex.Load(set);
					scorex.Calculate();
				}
				*/
			}
			return scoresets;
		}

		/// <summary>Adds a new score to an existing scorecard</summary>
		public XmlNode AddSet(string CardName, string SetName, string SetXml) {
			CheckSet(CardName, SetName); 
			XmlNode scoresets = GetSets(CardName);
			XmlNode scoreset = AddNode(scoresets, TYPE_SET, SetName);
			scoreset.InnerText = SetXml;
			addDate(scoreset as XmlElement);
			return scoreset;
		}

		/// <summary>Adds a new scorecard set to an existing scorecard case</summary>
		public XmlNode AddSet(string CardName, string SetName) {
			CheckSet(CardName, SetName);
			XmlNode parent = GetNode(TYPE_CARD, CardName, true);
			XmlNode scoreset = AddNode(TYPE_SET, SetName) as XmlElement;
			scoreset.AppendChild(CloneToChild(parent, TYPE_GROUP));
			addDate(scoreset as XmlElement);
			return GetSets(CardName).AppendChild(scoreset);
		}

		/// <summary>Adds a new scorecard set to an existing scorecard case</summary>
		public XmlNode DeleteSet(string CardName, string SetName) {
			XmlNode scoreset = GetSet(CardName, SetName);
			if (scoreset != null) {
				XmlNode parent = scoreset.ParentNode;
				_SetChanged(parent as XmlElement);
				scoreset = scoreset.ParentNode.RemoveChild(scoreset);
				//((XmlElement)scoreset).SetAttribute("deleted", "yes");
				scoreset = parent;
			}
			return scoreset;
		}

		/// <summary>Checks if a scorecard exists in a scorecard case</summary>
		public XmlNode CheckSet(string CardName, string SetName) {
			return CheckSet(CardName, SetName);
		}
		/// <summary>Checks scorecard set existance in a scorecard case</summary>
		public XmlNode CheckSet(string CardName, string SetName, bool exists) {
			XmlNode scoreset = GetSet(CardName, SetName);
			if (scoreset == null && !exists)
				throw new x_exception("ERROR_SET_NOTEXIST", ERROR_SET_NOTEXIST);
			if (scoreset != null && exists)
				throw new x_exception("ERROR_SET_EXISTS", ERROR_SET_EXISTS);
			return scoreset;
		}

		/// <summary>Removes a property from a scorecard set</summary>
		public void DeleteProperty(string CardName, string SetName, string PropName) {
			XmlNode setnode = GetSet(CardName, SetName);
			XmlNode propnode = GetChild(setnode, PropName, true);
			if (propnode != null) {
				XmlElement cardel = propnode.ParentNode as XmlElement;
				cardel.RemoveChild(propnode);
				_SetChanged(cardel);
			}
		}

		/// <overloads>Gets a property in a scorecard set</overloads>
		/// <summary>From card and set name</summary>
		public string GetProperty(string CardName, string SetName, string PropName) {
			XmlNode setnode = GetSet(CardName, SetName);
			return GetProperty(setnode, PropName);
		}
		/// <summary>From set node</summary>
		public string GetProperty(XmlNode SetNode, string PropName) {
			XmlNode propnode = SetNode.SelectSingleNode(string.Concat("property[@name='", PropName, "']"));	// move to x_tree? ie GetProperty
			string propval = "";
			if (propnode != null) {
				XmlNode valnode = propnode.SelectSingleNode("values/value");
				propval = (valnode == null)? "" : valnode.InnerText;
			}
			return propval;
		}

		/// <overloads>Gets a list (comma delimited) of properties in a scorecard set</overloads>
		/// <summary>From card and set name</summary>
		public string GetProperties(string CardName, string SetName, string PropName) {
			XmlNode setnode = GetSet(CardName, SetName);
			return GetProperties(setnode, PropName);
		}
		/// <summary>From set node</summary>
		public string GetProperties(XmlNode SetNode, string PropName) {
			XmlNode propnode = SetNode.SelectSingleNode(string.Concat("property[@name='", PropName, "']"));	// move to x_tree? ie GetProperty
			string propval = "";
			bool first = true;
			if (propnode != null) {
				foreach (XmlNode valnode in propnode.SelectNodes("values/value")) {
					string delim = (first) ? "" : ",";
					propval = String.Concat(propval, delim, valnode.InnerText);
					first = false;
				}
			}
			return propval;
		}

		/// <summary>Adds a new score to an existing scorecard</summary>
		public XmlNode UpdateSet(string CardName, string SetName, string Status, string SetXml) {
			XmlNode scoreset = CheckSet(CardName, SetName, false) as XmlElement;
			scoreset.InnerXml = SetXml;
			addDate(scoreset as XmlElement);
			addStatus(scoreset as XmlElement, Status);
			return scoreset;
		}

		#endregion

		#region Protected methods
		#endregion

		#region Private methods
		private XmlNodeList list(string type, string root, string data) {
			return GetNodes(type, false);	// no reference nodes for now??
		}
		private void addDate(XmlElement el) {
			el.SetAttribute("date", DateTime.Now.ToString("s", DateTimeFormatInfo.InvariantInfo));
		}
		// replace/add status string with enumerator?
		private void addStatus(XmlElement el, string status) {
			el.SetAttribute("status", status);
		}
		#endregion
	}
}
